// https://github.com/SamyPesse/draft-js-prism/blob/master/lib/index.js
import React from "react";
import Immutable from "immutable";
import Prism from "prismjs";

const KEY_SEPARATOR = "-";

function PrismDecorator(options) {
  this.highlighted = {};
}

/**
 * Return list of decoration IDs per character
 *
 * @param {ContentBlock}
 * @return {List<String>}
 */
PrismDecorator.prototype.getDecorations = function(block) {
  let tokens;
  let token;
  let tokenId;
  let resultId;
  let offset = 0;
  let tokenCount = 0;
  const blockKey = block.getKey();
  const blockType = block.getType();
  const blockData = block.getData();
  const blockText = block.getText();
  const decorations = Array(blockText.length).fill(null);
  const { highlighted } = this;

  highlighted[blockKey] = {};

  if (blockType !== "code-block") {
    return Immutable.List(decorations);
  }

  const syntax = blockData.get("syntax") || "javascript";

  // Allow for no syntax highlighting
  if (syntax == null) {
    return Immutable.List(decorations);
  }

  // Parse text using Prism
  const grammar = Prism.languages[syntax];
  tokens = Prism.tokenize(blockText, grammar);

  function processToken(decorations, token, offset) {
    if (typeof token === "string") {
      return;
    }
    // First write this tokens full length
    tokenId = `tok${tokenCount++}`;
    resultId = `${blockKey}-${tokenId}`;
    highlighted[blockKey][tokenId] = token;
    occupySlice(decorations, offset, offset + token.length, resultId);
    // Then recurse through the child tokens, overwriting the parent
    let childOffset = offset;
    for (let i = 0; i < token.content.length; i++) {
      const childToken = token.content[i];
      processToken(decorations, childToken, childOffset);
      childOffset += childToken.length;
    }
  }

  for (let i = 0; i < tokens.length; i++) {
    token = tokens[i];
    processToken(decorations, token, offset);
    offset += token.length;
  }

  return Immutable.List(decorations);
};

/**
 * Return component to render a decoration
 *
 * @param {String}
 * @return {Function}
 */
PrismDecorator.prototype.getComponentForKey = function(key) {
  return props => {
    const { type, children } = props;
    const className = `prism-token token ${type}`;
    return <span className={className}>{children}</span>;
  };
};

/**
 * Return props to render a decoration
 *
 * @param {String}
 * @return {Object}
 */
PrismDecorator.prototype.getPropsForKey = function(key) {
  const parts = key.split("-");
  const blockKey = parts[0];
  const tokId = parts[1];
  const token = this.highlighted[blockKey][tokId];

  return {
    type: token.type
  };
};

function occupySlice(targetArr, start, end, componentKey) {
  for (let ii = start; ii < end; ii++) {
    targetArr[ii] = componentKey;
  }
}

export default PrismDecorator;
